#!/usr/sbin/rsct/perl5/bin/perl
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 1999,2002 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
# "@(#)46   1.15   src/rsct/registry/cli/bin/findsr.perl, srcli, rsct_rpyxh, rpyxht1f3 2/22/01 16:25:43"
######################################################################
#                                                                    #
# Module: findsr                                                     #
#                                                                    #
# Purpose:                                                           #
#   findsr - Finds an entry in the System Registry                   #
#                                                                    #
# Syntax:                                                            #
#   findsr [-h][-l|d|D Delimiter][-F][-TV] Directory Expression      #
#                                                                    #
# Flags:                                                             #
#   -h     Help. Writes this command's usage statement to stdout.    #
#   -l     Display `long form' data. All available information is    #
#          shown.                                                    #
#   -d     Delimiter formatted output. The default delimiter is a    #
#          colon ":", use the -D flag if you wish to change the      #
#          default delimiter.                                        #
#   -D Delimiter                                                     #
#          Delimiter formatted output using the specified delimiter. #
#          Use this flag to specify something other than the default #
#          colon ":", when the data to be displayed may contain      #
#          colons. Use this flag to specify a one or more character  #
#          delimiter.                                                #
#   -F     Format. Puts a / (slash) after each entry name, if the    #
#          entry is a directory.  Puts a ? (question mark) after the #
#          name, if the entry is an unknown type (not a directory,   #
#          directory table or table).                                #
#   -T  Trace - -writes this program's trace messages to stderr.     #
#   -V  Verbose - writes this program's verbose messages to stderr.  #
#                                                                    #
# Operands:                                                          #
#   Directory   The name of the directory to search. A relative or   #
#               absolute path can be specified for the Directory     #
#               operand.                                             #
#   Expression  Defines attributes for the search. If the expression #
#               evaluates to true, then the command completes        #
#               successfully. Could be any of:                       #
#               - name <name> - specifies the name of the entry to   #
#               search for.                                          #
#               - storage <value> - evaluates to true if the storage #
#               type is value. Valid types are t and p (transient and#
#               persistent.)                                         #
#               - entry <value> - evaluates to true if the entry     #
#               type is value. Valid types are t and d (table and    #
#               directory .)                                         #
#                                                                    #
# Description:                                                       #
#   The findsr command recursively searches a given directory        #
#   attempting to evaluate an expression. The directory itself is    #
#   included in the search. If the expression evaluates to true, then#
#   the command completes successfully. (See under Operands for a    #
#   list of valid expressions.)                                      #
#                                                                    #
#   Expressions can use any or all of the flags -name, -entry,       #
#   -storage, with the restriction than only one occurrence of each  #
#   flag can be used per call. See the Examples section.             #
#                                                                    #
#   By default the name and path of the entries found satisfying the #
#   expression are printed. Paths displayed are relative to the given#
#   search directory. For more detailed output, use the -l flag      #
#   to display all currently available data for the entry (name,     #
#   entry type and storage type.) For the same information using     #
#   delimited output, use the -d flag to display a colon-delimiter   #
#   and the -D flag for user defined delimiter.                      #
#                                                                    #
#   Use the -F flag to format the output to show if an entry is a    #
#   table or a directory. (Note: This flag works with all other      #
#   format flags.)                                                   #
#                                                                    #
# Exit Values:                                                       #
#   0  SR_CLI_SUCCESS        Command completed successfully.         #
#   1  SR_CLI_REGISTRY_ERROR Command terminated due to an underlying #
#                            System Registry error.                  #
#   2  SR_CLI_ERROR          Command terminated due to an underlying #
#                            error in the command script.            #
#   3  SR_CLI_BAD_OPERAND    Command terminated due to user          #
#                            specifying a bad operand.               #
#   4  SR_CLI_BAD_FLAG       Command terminated due to user          #
#                            specifying an invalid flag.             #
#   5  SR_CLI_USER_ERROR     Command terminated due to a user error. #
#                            For example specifying an undefined     #
#                            directory to be listed.                 #
#                                                                    #
# Examples:                                                          #
#                                                                    #
#   1. To find the entry Resources in the current directory tree,    #
#   where the current directory (via the CT_SR_HOME environment      #
#   variable) has been set to /IBM enter:                            #
#       findsr . -name Class                                         #
#                                                                    #
#   2. To locate all the persistent entries under the /IBM/Cluster   #
#   directory (including /IBM/Cluster), enter:                       #
#       findsr /IBM/Cluster -storage p                               #
#                                                                    #
#   3. To display the storage type of all entries under the current  #
#   directory (including the current directory), enter:              #
#       findsr -l . -name "*"                                        #
#                                                                    #
#   4. To find all directories beginning with C in the directory     #
#   tree starting from the current working directory  (the           #
#   environment variable CT_SR_HOME is set to /samples), enter:      #
#       findsr ./ -name "C*" -entry d                                #
#  Note: missing the quotes around the wildcarded name will result   #
#  in AIX returning 'No match.' Compare this to 'No match found.',   #
#  which is a return code for this command if it does not locate     #
#  any matches for the given expression.                             #
#                                                                    #
#   5. To display all entries that begin with A under the current    #
#   directory, using colon-delimited output and  displaying their    #
#   entry characteristics, enter:                                    #
#       findsr -Fd . -name "A*"                                      #
#                                                                    #
#--------------------------------------------------------------------#
# Inputs:                                                            #
#                                                                    #
# Outputs:                                                           #
#   stdout - messages during execution and Verbose output            #
#   stderr - error messages                                          #
#                                                                    #
# External Ref:                                                      #
#   Commands: $LSMSG                                                 #
#   Extensions:  CT::SR.pm CT::SRrc.pm                               #
#   Perl library routines: Getopts::Std                              #
#   SR cli routines: : SR_cli_utils.pm - init_session, isRelative    #
#                        error_exit, term_session, printCEMsg        #
#                      SR_cli_rc.pm - CLI return codes               #
#                                                                    #
# Tab Settings:                                                      #
#   4 and tabs should be expanded to spaces before saving this file. #
#   in vi:  (:set ts=4  and  :%!expand -4)                           #
#                                                                    #
# Change Activity:                                                   #
#   000929 HGJ 38317: Initial delivery.                              #
#                                                                    #
######################################################################

#--------------------------------------------------------------------#
#                                                                    #
# General Program Flow/Logic:                                        #
#                                                                    #
# A: Parse command line - get table name and input file              #
# B: Initialise session with registry, including changing the        #
#    current directory if a relative path name is given (use value   #
#    given in CT_SR_HOME)                                            #
# C: Parse directory, if necessary                                   #
# D: Sort expression so the find algorithm can easily parse it       #
# E: Call CT::SR::get_directory_list to get the list to search       #
# F: Evaluate the expression against the list and print the result   #
#    to standard out                                                 #
# F: Close session table and tree                                    #
#                                                                    #
#--------------------------------------------------------------------#


#--------------------------------------------------------------------#
# Included libraries and extensions                                  #
#--------------------------------------------------------------------#
use lib "/usr/sbin/rsct/pm";
use locale;
use Getopt::Std;
use Env qw( CT_SR_HOME );

use CT_cli_utils qw(printIMsg
                    printEMsg 
);

use CT::SRrc;
use CT::SR;
use CT::SR qw ( :sr_storage_t :sr_entry_t );
use SR_cli_utils qw(init_session 
                    isRelative 
                    term_session 
                    printCEMsg 
                    $DEFAULT_GLOBAL_MOUNT_POINT
                    error_exit
); 
use SR_cli_rc qw( :return_codes );


#--------------------------------------------------------------------#
# Global Variables                                                   #
#--------------------------------------------------------------------#
# Constants
$TRUE           = 1;
$FALSE          = 0;
$NO_MATCH       = 10;

# Variables to remap opt_x variables produced by getopts
# These are used globally by all subroutines called by the
# main program.
$Opt_Long       = $FALSE;           # default - see -l (long form)
$Opt_Delim      = $FALSE;           # default - see -D | -d 
$Opt_Format     = $FALSE;           # default - see -F 
$Verbose        = $FALSE;           # default - see -V
$Trace          = $FALSE;           # default - see -T

# Messaging variables
$PROGNAME       = "findsr";
$MSGCAT         = "srcli.cat";
$CTDIR          = "/usr/sbin/rsct";      # Cluster directory path
$CTBINDIR       = "$CTDIR/bin";          # Cluster bin directory path
$LSMSG          = "$CTBINDIR/ctdspmsg";  # Display message routine
$ENV{'MSGMAPPATH'} = "$CTDIR/msgmaps";   # Msg maps path for $LSMSG  

%Cleanup = ();                      # Hash of items to cleanup
                                    # {Session} $session to term

#--------------------------------------------------------------------#
# Variables                                                          #
#--------------------------------------------------------------------#
# Variables for use with extensions
my $Dir_list       = CT::SR::entry_metadata_t->new;

# Initialised to CT::SR::tree_handle_t class in init_session
my $Tree_handle    = "";

my $Set_work_dir   = $FALSE;           
my $Command_line_input = "";
my $Directory      = "";
my $Dir            = "";
my @Expression     = ();
my @Exp_array      = ();
my $rc             = 0;                # assume good return code
my $jrc            = 0;                # used to preserve $rc

my $Mount_point    = $DEFAULT_GLOBAL_MOUNT_POINT;


#--------------------------------------------------------------------#
# Main Code                                                          #
#--------------------------------------------------------------------#
# TODO: Many verbose statements in this code will eventually by
# Trace statements when the facility is available as a Perl CLI
# (feature 48401)
# TODO: security on access to the table can not be further defined
# until after a security design has been implemented for the SR.
# ( feature 48402 ) Until then, the table is opened with the
# minimum security necessary to complete the command.

# Parse the command line, exit if there are errors
# Error messages handled in subroutine
($rc, $Directory, @Expression) = parse_cmd_line();  
($rc == 0) || error_exit($rc); 

# Verbose statement echoing the command line input
if ($Verbose) {
    $Command_line_input = "  $Directory\n";
    foreach (@Expression) {
        $Command_line_input .= "  $_\n"; 
    }
    printIMsg("IMsgfindsrCommandLineInput");
    print $Command_line_input, "\n";;   
}


# Parse the directory to parse any ".", "./", or "../" into more
# meaningful directory (path) names for the extension call.

$Trace && print STDERR "Entering parse_directory($Directory)\n";
($rc, $Parsed_dir, $CWDirectory) = parse_directory($Directory);
$Trace && print STDERR "parse_directory return code $rc\n";
($rc == 0) || error_exit($rc);


# Initialize variables for init_session - check to see if an
# absolute or relative directory has been passed in.
if (isRelative($Parsed_dir)) {
    $Set_work_dir= $TRUE;
    $Dir = $Parsed_dir;
    $CWDirectory = '/'.$Parsed_dir;
}
else {
    $Dir = $Mount_point.$Parsed_dir;
    $CWDirectory = $Parsed_dir;
}


$Verbose && printIMsg("IMsgfindsrDirectoryList", $Directory, 
                        $Parsed_dir, $CWDirectory);

# Initialize Registry Library Connection
# Assume relevant error messages handled in init_session
($rc,$Tree_handle) = init_session($Set_work_dir);
($rc == 0) || error_exit($rc);

# Add Tree handle to cleanup hash
$Cleanup{Session} = $Tree_handle;


# Parse and check the input Expression
if (scalar(@Expression) > 6) {
    printEMsg("EMsgfindsrTooManyOperands");
    exit SR_CLI_BAD_OPERAND;
}


# Need to re-order the array so the find algorithm can use it
$Trace && print STDERR "Entering order_expression(@Expression)\n";
($rc, @Exp_array) = order_expression(@Expression);
$Trace && print STDERR "order_epxression return code $rc\n";
($rc == 0) || error_exit($rc);


# Grab the recursive listing of the directory tree starting at the 
# requested level
$Trace && print STDERR "Calling CT::SR::get_directory_list\n";

# The $TRUE arg here is for recursive list
$rc = CT::SR::get_directory_list($Tree_handle, $Dir, $TRUE, $Dir_list);
$Trace && print STDERR "SR::get_directory_list return code: $rc\n";

# Check the result of the registry call
$rc = error_check("sr_get_directory_list", $rc, $Directory);
($rc == 0) || error_exit($rc);


# Now the list is ordered and the directory list received,
# call the subroutine to check and display matches 
$Trace && print STDERR "Entering evaluate_expression\n";
$rc = evaluate_expression($CWDirectory, $Directory, $Dir_list, 
                        @Exp_array); 
$Trace && print STDERR "evaluate_expression return code $rc\n";

# This should be self-explanatory. No match is considered a 
# valid outcome of this command (return code 0.) 
if ($rc == $NO_MATCH) {
    printIMsg("IMsgfindsrNoMatch");
    $rc = 0;
}

# If anything other than $NO_MATCH comes out of the subroutine,
# then there has been an operand error.
($rc == 0) || error_exit(SR_CLI_BAD_OPERAND);


# Free the memory used for the directory list
# If no entries were found, then the directory list had
# no memory allocated to it to be freed.
if ($Dir_list->get_array_count != 0) {
    $Trace && print STDERR "Calling CT::SR::free_directory_list\n";
    $rc = CT::SR::free_directory_list($Dir_list);
    $Trace && 
        print STDERR "CT::SR::free_directory_list return code: $rc\n";

    $rc = error_check("sr_free_directory_list", $rc, $Directory);
    ($rc == 0) || error_exit($rc);
}

# Even if the find failed, clean up session
$rc = term_session($Tree_handle, $Mount_point);
%Cleanup = ();
($rc == 0) || error_exit($rc);

exit $rc;

#--------------------------------------------------------------------#
# End Main Code                                                      #
#--------------------------------------------------------------------#


#--------------------------------------------------------------------#
# parse_directory:                                                   #
#    This routine is only pertinent if the directory is relative, in #
#    order to parse . and .. in the directory path. The altered      #
#    directory name is made absolute.                                #
#                                                                    #
# Parameters:                                                        #
#   $dir         - directory as entered on the command line          #
#                                                                    #
# Return values:                                                     #
#   $local_rc - local return code                                    #
#   $dir      - directory either untouched or with leading ../       #
#               or ../ or . removed/replaced and made absolute       #
#                                                                    #
# Global variables modified:                                         #
#   None.                                                            #
#--------------------------------------------------------------------#
sub parse_directory 
{
# Grab input parameter
my ($dir) = @_;

# Set up local variables and patterns
my $current_working_dir = "";
my $local_rc = 0;
my $up_one_pattern = '\.\.\/';
my $current_dir_pattern = '\.\/';
my $alternate_pattern = '\.';

# Assume current working directory is the root directory to start
my $cwd = '/';

if (isRelative($dir)) {

    if (!$CT_SR_HOME) {
        # Directory has not been set
        if ($dir =~ /^$up_one_pattern/) {
            printEMsg("EMsgfindsrIllegalSearchDir");
            return SR_CLI_BAD_OPERAND;
        }
    }
    else { $cwd = $CT_SR_HOME; }

    # Strip off any leading special patterns
    while ( ($dir =~ /^$current_dir_pattern/) or 
            ($dir =~ /^$up_one_pattern/) or
            ($dir =~ /^$alternate_pattern/) ) {

        # It's either "../" or "./" or "."

        if ($dir =~ /^$up_one_pattern/) {
            # Is the current directory
            # remove the leading "../"
            $dir =~ s/^($up_one_pattern){1}(.*)/$2/;

            if  ($cwd eq '/') {
                printEMsg("EMsgfindsrIllegalSearchDir");
                return SR_CLI_BAD_OPERAND;
            }
            else {
                # Strip off the rightmost member of the directory path
                # to accomodate the "../"
                $cwd =~ s/^(.*)\/.*$/$1/;
                if ($cwd eq "") {$cwd = '/';}
            }

        }
        elsif ($dir =~ /^$current_dir_pattern/) {
            # Is the current directory
            # remove the leading "./"
            $dir =~ s/^$current_dir_pattern(.*)/$1/;
        }
        elsif ($dir =~ /^$alternate_pattern$/) {
            # Is the current directory
            # replace "." with "/" or $CT_SR_HOME as appropriate
            if ($CT_SR_HOME) { $dir = $CT_SR_HOME; }
            else { $dir = '/';}
        }
    }

    if ($cwd ne $CT_SR_HOME) {
        # Dir name has been changed, so make the directory 
        # name absolute
        if ($cwd eq "") { $dir = '/'.$dir; }
        elsif ($dir ne $cwd) { $dir = $cwd.$dir; }
    }

}

return ($local_rc, $dir, $cwd);
}   # end parse_directory


#--------------------------------------------------------------------#
# order_expression                                                   #
#   Orders the input array @expression : name, type, storage         #
#   in order for the find algorithm to parse the request.            #
#                                                                    #
# Parameters:                                                        #
#   @expression - array containing expression components grabbed     #
#                 from command line                                  #
#                                                                    #
# Return values:                                                     #
#   $local_rc  - local return code                                   #
#   @exp_array - array containing ordered version of the input       #
#                array                                               #
#                                                                    #
# Global variables used:                                             #
#   None.                                                            #
#--------------------------------------------------------------------#
sub order_expression 
{
my @expression = @_;

# Set up local variables
my @exp_array = ($FALSE, $FALSE, $FALSE, $FALSE, $FALSE, $FALSE);
my $local_rc = 0;
my ($find_flag, $find_value);

while (@expression) {
    # Expecting the data in pairs
    $find_flag = shift @expression;
    $find_value = shift @expression;

    if ($find_flag =~ /-name/) {
        if ($exp_array[0]) {
            printEMsg("EMsgfindsrTooManyOperands");
            return SR_CLI_BAD_OPERAND;
        }
        $exp_array[0] = $find_flag;
        $exp_array[1] = $find_value;
    }
    elsif ($find_flag=~ /-entry/) {
        if ($exp_array[2]) {
            printEMsg("EMsgfindsrTooManyOperands");
            return SR_CLI_BAD_OPERAND;
                
        }
        $exp_array[2] = $find_flag;
        $exp_array[3] = $find_value;
    }
    elsif ($find_flag =~ /-storage/) {
        if ($exp_array[4]) {
            printEMsg("EMsgfindsrTooManyOperands");
            return SR_CLI_BAD_OPERAND;
        }
        $exp_array[4] = $find_flag;
        $exp_array[5] = $find_value;
    }
    else {
        printEMsg("EMsgfindsrIllegalExpression", $find_flag, 
                        $find_value);
        return SR_CLI_BAD_OPERAND;
    }
}

return ($local_rc, @exp_array);
}   # end order_expression


#--------------------------------------------------------------------#
# evaluate_expression                                                #
#   Attempts to evaluate the given expression against the given      #
#   directory list. Outputs entries matching the expression based    #
#   on flags used on the command line. Output is written to a temp   #
#   variable so it can be parsed for duplicate entries before being  #
#   written to standard out.                                         #
#                                                                    #
# Parameters:                                                        #
#   $cwd         - current working directory                         #
#   $dir_name    - directory as entered on the command line          #
#   $dir_list    - CT::SR::entry_metadata_t pointer containings list #
#                of the directory (tree) to be searched              #
#   @expression  - expression to use for the search                  #
#                                                                    #
# Return values:                                                     #
#   $local_rc - local return code                                    #
#                                                                    #
# Global variables used:                                             #
#   $Opt_Long, $Opt_Delim - for output options                       #
#--------------------------------------------------------------------#
sub evaluate_expression 
{
# Grab input parameters
my ($cwd, $dir_name, $dir_list, @expression) = @_;

# Chop off trailing '/' on directory name for display
$dir_name =~ s/^(.*)\/$/$1/;

# Set up local variables
# Assume a bad return code to begin with, 
my $local_rc = $NO_MATCH;
my $dir_containers = "";
my $pattern = "";
my $output = "";
my ($name, $type, $storage);

# Hashes for interpreting command line input
# These hashes have to be kept up to date with the headers!
my %entry_hash = (
        0 => "Unknown",
        1 => "Table",
        2 => "Directory",       # True Directory
        3 => "Mount_point",
        4 => "Directory"        # Directory Container
);

my %storage_hash = (
        1 => "Unknown",
        1 => "Persistent",
        2 => "Transient",
        3 => "Remote"
);

my $num_entries = $dir_list->get_array_count;
for ($i = 0 ; $i < $num_entries ; $i++) {

    # Grab the name from the entry metadata
    # It is displayed by default for all output methods
    $name = $dir_list->getName($i);

    # Make the name relative to the directory given on the command 
    # line

    if ($cwd eq '/') { $name =~ s/^$cwd(.*)$/$1/; }
    else { $name =~ s/^$cwd\/(.*)$/$1/; }
    $name = $dir_name.'/'.$name;
    
    # Grab the entry type from the entry metadata
    $type = $dir_list->getType($i);

    # Evaluate the expression
    if ($expression[0]){
        # Check the name

        $pattern = $expression[1];
        
        # Substitute any wildcards "*" with the wildcard pattern ".*"
        $pattern =~ s/\*/\.\*/g;

        # Verify the pattern matches or go to the next entry
        if ($name !~ /$pattern$/) { next; }
    }

    if ($expression[2]) {
        # Check the entry type

        # Parse the entry type to be searched for
        my $entry_type = 0; 
        if ( $expression[3] eq 't') {
            $entry_type = SR_TABLE;
        }
        elsif ($expression[3] eq 'd') {
            $entry_type = SR_DIRECTORY;
        }
        elsif ($expression[3] eq 'c') {
            $entry_type = SR_DIRECTORY_CONTAINER;
        }
        else {
            printEMsg("EMsgfindsrIllegalExpression", $expression[2], 
                        $expression[3]);
            return SR_CLI_BAD_OPERAND;
        }
        if ($entry_type != $type) { next; }
    }
    if ($expression[4]) {

        # Grab the storage type from the entry metadata
        $storage = $dir_list->getStorage($i);

        # Set up the storage type to be scanned for
        my $storage_type = 0;
        if ( $expression[5] eq 'p') {
            $storage_type = SR_PERSISTENT;
        }
        elsif ($expression[5] eq 't') {
            $storage_type = SR_TRANSIENT;
        }
        else {
            printEMsg("EMsgfindsrIllegalExpression", $expression[4], 
                        $expression[5]);
            return SR_CLI_BAD_OPERAND;
        }

        if ($storage_type != $storage) { next; }

    }

    if ($Opt_Long) {
    
        # Get any missing information
        if (!$expression[4]) { $storage = $dir_list->getStorage($i); }

        # This needs to preceed the hash table accesses because
        # $type needs to be numeric still

        if ($Opt_Format) { $name = append_entry_type($name, $type);}

        $entry_type = $entry_hash{$type};
        $storage = $storage_hash{$storage};

        # Directory entires are duplicated in the list data
        # so the duplicates must be scanned for and removed
        if ($type == SR_DIRECTORY) { $dir_containers .= " $name"; }
        if (($type == SR_DIRECTORY_CONTAINER) and 
            ($dir_containers =~ /$name/)) { 

            # Scan for and remove any duplicate entries as they will 
            # be out of order in the list
            $pattern = "$name:\n\ttype    = $entry_type\n\tstorage = $storage\n\n";
            $output =~ s/$pattern//;

        }


        # NLS note: the hard coded names here correspond to the values
        # used in the structure itself, so are used literally here and
        # do not need translation

        $output .= "$name:\n\ttype    = $entry_type\n\tstorage = $storage\n\n";

    }           
    elsif ($Opt_Delim) { 
        # Print the output using the current delimiter

        if ($Opt_Format) { $name = append_entry_type($name, $type);}

        # Directory entires are duplicated in the list data
        # so the duplicates must be scanned for and removed
        if ($type == SR_DIRECTORY) { $dir_containers .= " $name"; }
        if (($type == SR_DIRECTORY_CONTAINER) and 
            ($dir_containers =~ /$name/)) { 

            # Scan for and remove any duplicate entries as they will 
            # be out of order in the list
            $pattern = "$name$Opt_Delim";
            $output =~ s/$pattern//;

        }
        $output .= $name.$Opt_Delim;
    }
    else { 
        # Print the name to stdout                         

        if ($Opt_Format) { $name = append_entry_type($name, $type);}

        # Directory entires are duplicated in the list data
        # so the duplicates must be scanned for and removed
        if ($type == SR_DIRECTORY) { $dir_containers .= " $name"; }
        if (($type == SR_DIRECTORY_CONTAINER) and 
            ($dir_containers =~ /$name/)) { 

            # Scan for and remove any duplicate entries as they'll be
            # out of order in the list. Collapse remaining \n\n to \n.
            $output =~ s%^$name$%%m;
            $output =~ s%\n\n%\n%;

        }
        $output .= $name."\n";
    }

    # Assumption: if no matches are found, this line is never reached,
    # so the initial value of $local_rc = 1 will be passed out
    $local_rc = 0;

}


# Chop off any leading \n that may have been left by removing entries
# from the output string, then print the output
$output =~ s/^\n//;
print $output;

# Add one last carriage return is is delimited output so the cursor
# ends up on the next line
if ($Opt_Delim) { print "\n"; }

return $local_rc;
}   # end evaluate_expression


#--------------------------------------------------------------------#
# append_entry_type:                                                 #
#   Adds a marker onto the end of an entry to denote it's type:      #
#   directory (/), containing directory (/c), or unknown (?)         #
#   Table entries are left untouched.                                #
#                                                                    #
# Parameters:                                                        #
#   $entry_name - name of the entry to be modified                   #
#   $entry_type - type of the entry given                            #
#                                                                    #
# Return values:                                                     #
#   $entry_name - possibly modified entry (not changed if is a table #
#                 entry)                                             #
#                                                                    #
# Global variables modified:                                         #
#   None.                                                            #
#--------------------------------------------------------------------#
sub append_entry_type 
{
my ($entry_name, $entry_type) = @_;
my $local_rc = 0;

if ($entry_type == SR_DIRECTORY) {             # directories get a "/"
    $entry_name = $entry_name . '/';
}
elsif ($entry_type == SR_DIRECTORY_CONTAINER) {# directories get a "/"
    $entry_name = $entry_name . '/';
}
elsif ($entry_type == SR_UNKNOWN_ENTRY) {      # unknowns get a "?"
    # This is considered a severe registry error
    $entry_name = $entry_name . "?";
    $local_rc = SR_CLI_REGISTRY_ERROR;  
}                                              # tables left as is

return $local_rc, $entry_name;
}   # end append_entry_type


#--------------------------------------------------------------------#
# parse_cmd_line:                                                    #
#   Uses getopts() to grab flags on the command line, sets globals   #
#   according to the flags, grabs the rest of the input (giving an   #
#   error if there is not enough data.) Returns the command line     #
#   data.                                                            #
#                                                                    #
# Return values:                                                     #
#   $local_rc   - local return code                                  #
#   $directory  - name of directory as entered on command line       #
#   @expression - array containing elements of the expression as     #
#               entered on the command line                          #
#                                                                    #
# Global variables modified:                                         #
#   $Opt_Format        output   True (-F) format flag (/ after dir..)#
#   $Opt_Long          output   True (-l) print one entry per line   #
#   $Opt_Delm          output   Delimitter string (-d|-D delimitter) #
#                               print delimitter separated output.   #
#   $Trace             output   True (-T) turn Trace mode on.        #
#   $Verbose           output   True (-V) turn Verbose mode on.      #
#--------------------------------------------------------------------#
sub parse_cmd_line
{
my $local_rc = 0;
my %opts = ();

if (getopts('dD:FhlTV', \%opts) == 0) {         # parse input flags
    # Return Error parsing flags - add a message here, too.
    printCEMsg("EMsgSRcliInvalidFlag");
    print_usage();
    return SR_CLI_BAD_FLAG;
}                  

if (defined $opts{h}) {   # print usage and exit
    print_usage();
    exit(0);    # exit quickly
}

# Set Trace flag if requested
if (defined $opts{T}) {
    $Trace = $TRUE;
}

# Set Verbose flag if requested
if (defined $opts{V}) {
    $Verbose = $TRUE;
}

# Set Format flag if requested
if (defined $opts{F}) {
    $Opt_Format = $TRUE;
}

# Set long display flag if requested
if (defined $opts{l}) {
    $Opt_Long = $TRUE;
}

# Set colon delimited output flag if requested
if (defined $opts{d}) {
    $Opt_Delim = ':';
}

# Set delimited output flag if requested
if (defined $opts{D}) {
    $Opt_Delim = $opts{D};
}

if (!@ARGV) {
    printEMsg("EMsgfindsrIllegalSearchDir");
    print_usage();
    return SR_CLI_BAD_OPERAND;
}

# Get table name from input.
my $directory = shift @ARGV;

if (!@ARGV) {
    printEMsg("EMsgfindsrIllegalExpression", '"(not"', '"found)"');
    print_usage();
    return SR_CLI_BAD_OPERAND;
}

if (scalar(@ARGV) < 2) {
    printEMsg("EMsgfindsrIncompleteExp");
    print_usage();
    return SR_CLI_BAD_OPERAND;
}   

# Assume the expression is the rest of the input
my @expression = @ARGV;

return($local_rc, $directory, @expression);
}   # end parse_cmd_line


#--------------------------------------------------------------------#
# error_check:                                                       #
#   Checks the return code from the SR function.  If an error is     #
#   detected appropriate error messages will be displayed and        #
#   SR CLI return code set.                                          #
#                                                                    #
# Parameters:                                                        #
#   $sr_function  - Name of the SR function that was called and      #
#                   whose error code we are checking.                #
#   $sr_rc        - SR function return code.                         #
#   $directory    - Name of the directory.                           #
#                                                                    #
# Return values:                                                     #
#   None.                                                            #
#                                                                    #
# Global References:                                                 #
#   None.                                                            #
#--------------------------------------------------------------------#
sub error_check
{
my ($sr_function, $sr_rc, $directory) = @_;
my $rc = 0;

if ($sr_rc != 0) {
    if ($sr_rc == SR_NO_PERMISSION) {
        printEMsg("EMsgfindsrNoPermission", $directory);
        $rc = SR_CLI_USER_ERROR;
    }
    elsif ($sr_rc == SR_NO_DIRECTORY) {
        printEMsg("EMsgfindsrNoDirectory", $directory);
        $rc = SR_CLI_USER_ERROR;
    }
    elsif ($sr_rc == SR_CONNECTION_LOST) {
        # At this point, there is nothing else that can be done.
        printCEMsg("EMsgSRcliConnectLost");
        $rc = SR_CLI_REGISTRY_ERROR;
    }
    elsif ($sr_function eq "sr_get_directory_list") {
        # Here, something else may be possible for cleanup
        printEMsg("EMsgfindsrSearchError", $Directory);
        printCEMsg("EMsgSRcliSRCommandFailure", $sr_function, $sr_rc);
        $rc = SR_CLI_REGISTRY_ERROR;
    }
    elsif ($sr_function eq "sr_free_directory_list") {
        printEMsg("EMsgfindsrFreeDirListError", $Directory);
        printCEMsg("EMsgSRcliSRCommandFailure", $sr_function, $sr_rc);
        $rc = SR_CLI_REGISTRY_ERROR;
    }
    else {
        $rc = SR_CLI_REGISTRY_ERROR;
    }
}       

return $rc;
}   # end error_check


#--------------------------------------------------------------------#
# print_usage : print the usage statement (syntax) to stdout.        #
#   See this command's prologue syntax section for current usage.    #
#--------------------------------------------------------------------#
sub print_usage
{
printIMsg("IMsgfindsrUsage");
} # end print_usage


#--------------------------------------------------------------------#
# End File                                                           #
#--------------------------------------------------------------------#
